package vn.edu.hcmut.cse.ai.search.problems.npuzzle;

import java.util.Arrays;

public class NPuzzleState {

    // The size of the board (total cell number is size * size)
    private final int size;

    // The 2-dimensional board
    private final int[][] board;

    // These stores the position of the gap
    private int gapXPosition;
    private int gapYPosition;

    // Assume that size and board both are valid inputs
    // There is no input validity checking
    public NPuzzleState(int size, int[][] board) {
        this.size = size;
        this.board = new int[size][size];
        for (int row = 0; row < size; row++) {
            System.arraycopy(board[row], 0, this.board[row], 0, size);
        }

        // Finf position of the gap
        for (int row = 0; row < size; row++) {
            for (int col = 0; col < size; col++) {
                if (board[row][col] == 0) {
                    this.gapXPosition = col;
                    this.gapYPosition = row;
                }
            }
        }
    }

    public NPuzzleState(NPuzzleState copyState) {
        this(copyState.getSize(), copyState.getBoard());
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 41 * hash + this.size;
        hash = 41 * hash + Arrays.deepHashCode(this.board);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final NPuzzleState otherBoard = (NPuzzleState) obj;
        if (Arrays.deepEquals(this.board, otherBoard.getBoard())) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public String toString() {  // TODO: implement this
        return "NPuzzleState{" + "gapXPosition=" + gapXPosition + '}';
    }

    public int[][] getBoard() {
        return board;
    }

    // Move left
    public boolean moveLeft() {
        if (this.gapXPosition > 0) {
            this.board[this.gapYPosition][this.gapXPosition] = this.board[this.gapYPosition][--this.gapXPosition];
            this.board[this.gapYPosition][this.gapXPosition] = 0;
            return true;
        } else {
            return false;
        }
    }

    // Move right
    public boolean moveRight() {
        if (this.gapXPosition < (size - 1)) {
            this.board[this.gapYPosition][this.gapXPosition] = this.board[this.gapYPosition][++this.gapXPosition];
            this.board[this.gapYPosition][this.gapXPosition] = 0;
            return true;
        } else {
            return false;
        }
    }

    // Move up
    public boolean moveUp() {
        if (this.gapYPosition > 0) {
            this.board[this.gapYPosition][this.gapXPosition] = this.board[--this.gapYPosition][this.gapXPosition];
            this.board[this.gapYPosition][this.gapXPosition] = 0;
            return true;
        } else {
            return false;
        }
    }

    // Move down
    public boolean moveDown() {
        if (this.gapYPosition < (size - 1)) {
            this.board[this.gapYPosition][this.gapXPosition] = this.board[++this.gapYPosition][this.gapXPosition];
            this.board[this.gapYPosition][this.gapXPosition] = 0;
            return true;
        } else {
            return false;
        }
    }

    public int getSize() {
        return size;
    }

    public int getGapXPosition() {
        return gapXPosition;
    }

    public int getGapYPosition() {
        return gapYPosition;
    }

}
